﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace XNA_Game_Framework
{
	/// <summary>
	/// Class for setting up a screen.
	/// A screen fills the entire window, and there can never be more than one screen in view
	/// at a time. If there are "subscreens", the subscreens are actually Components.
	/// 
	/// Navigation from screen to screen is handled with a ScreenManager.
	/// </summary>
	public class Screen
	{
		private ContentManager contentManager;
		internal GameTime gameTime;
		private GraphicsDeviceManager graphics;
		private InputHandler inputHandler;
		protected ScreenManager myScreenManager;
		private SpriteBatch spriteBatch;
		//private ThemeManager themeManager;
		
		protected Game gameRef;
		
		private Color backgroundColor;
		private bool enabled;
		private string name;
		private List<Container> containers;
		private List<RightClickMenu> rightClickMenus;

		#region properties
		/// <summary>
		/// Gets or sets the background color of the screen
		/// </summary>
		public Color BackgroundColor
		{
			get { return backgroundColor; }
			set { backgroundColor = value; }
		}

		public ContentManager Content
		{
			get { return contentManager; }
		}

		public bool Enabled
		{
			get { return enabled; }
			set { enabled = value; }
		}

		/// <summary>
		/// Used by ScreenManager to set the GraphicsDeviceManager.
		/// </summary>
		internal GraphicsDeviceManager Graphics
		{
			get { return graphics; }
			set { graphics = value; }
		}

		public int Height
		{
			get { return graphics.PreferredBackBufferHeight; }
		}

		/// <summary>
		/// Gets the InputHandler for this screen. Handle with care as it contains the input handling for all controls on the screen.
		/// </summary>
		public InputHandler InputHandler
		{
			get { return inputHandler; }
		}

		/// <summary>
		/// Gets or sets the name of the screen
		/// </summary>
		public string Name 
		{
			get { return name; }
			set { name = value; }
		}

		public ThemeManager Themes
		{
			get { return myScreenManager.Themes; }
		}

		public int Width
		{
			get { return graphics.PreferredBackBufferWidth; }
		}

		internal List<Container> Windows
		{
			get { return containers; }
		}
		#endregion

		public Screen(Game game, ScreenManager screenManager, string name)
		{
			SetUpScreen(game, null, screenManager, null, name);
		}
		
		public Screen(Game game, GraphicsDeviceManager graphics, ScreenManager screenManager, ThemeManager themes, string name) 
		{
			SetUpScreen(game, graphics, screenManager, themes, name);
		}

		private void SetUpScreen(Game game, GraphicsDeviceManager graphics, ScreenManager screenManager, ThemeManager themes, string name)
		{
			gameRef = game;
			this.graphics = graphics;
			this.inputHandler = new InputHandler("MainInputHandler");
			myScreenManager = screenManager;
			//themeManager = themes;
			spriteBatch = new SpriteBatch(game.GraphicsDevice);

			contentManager = game.Content;

			Name = name;
			BackgroundColor = Color.CornflowerBlue;
			enabled = true;

			containers = new List<Container>();
			rightClickMenus = new List<RightClickMenu>();

            inputHandler.LeftButtonPressed += OnLeftClick;
			inputHandler.RightButtonPressed += OnRightClick;
            inputHandler.MouseMoved += OnMouseMoved;
			inputHandler.MouseLeftDrag += OnMouseLeftDragged;
		}

		#region Add Window
		public Container AddContainer(string windowName, Vector2 position, Vector2 size)
		{
			Container newWindow = new Container(windowName, this, position, size);
			containers.Add(newWindow);
			return newWindow;
		}

		public DialogueAuto AddDialogue(String name, Vector2 position, String text, DialogueAuto.Buttons buttons, string dialogueTheme)
		{
			Theme.Dialogue dialogueThemeObject = (Theme.Dialogue)Themes.GetTheme(dialogueTheme);
			DialogueAuto newDialogue = new DialogueAuto(name, this, position, text, buttons, dialogueThemeObject);
			containers.Add(newDialogue);
			return newDialogue;

		}

		public WindowVar AddWindowVar(string windowName, string title, Vector2 position, Vector2 size, string theme)
        {
			Theme.WindowVar windowTheme = (Theme.WindowVar)Themes.GetTheme(theme);
			WindowVar newWindow = new WindowVar(name, this, title, position, size, windowTheme);
			containers.Add(newWindow);
            return newWindow;
        }

		/// <summary>
		/// Adds a RightClickMenu to the container. The RightClickMenu will not be visible until the control is actually right-clicked.
		/// </summary>
		/// <param name="addTo">The control to add it to</param>
		/// <param name="name">The unique system name of the control</param>
		/// <param name="entries">Array of entries in the menu, from first to last</param>
		/// <param name="font">Font of the entries</param>
		/// <param name="topBorder">Texture for the top border of the menu</param>
		/// <param name="sideBorders">Texture for the bulk of the menu.</param>
		/// <param name="bottomBorder">Texture for the bottom border of the menu</param>
		/// <param name="selectionBox">Texture of the selection marker</param>
		/// <returns></returns>
		public RightClickMenu AddRightClickMenu(Control addTo, string name, string[] entries, SpriteFont font,
			Texture2D topBorder, Texture2D sideBorders, Texture2D bottomBorder, Texture2D selectionBox)
		{
			RightClickMenu rightclickmenu = new RightClickMenu(this, addTo, inputHandler, name, new Vector2(0, 0), entries, font, topBorder, sideBorders, bottomBorder, selectionBox);

			addTo.SetRightClickMenu(rightclickmenu);
			rightClickMenus.Add(rightclickmenu);

			return rightclickmenu;
		}
		#endregion

		#region ContentHandling
		protected Texture2D LoadTexture2D(string path)
		{
			return Content.Load<Texture2D>(@path);
		}
		#endregion

		#region Control and Container Handling
		/// <summary>
		/// Return the named container
		/// </summary>
		/// <param name="containerName">The system name of the container</param>
		/// <returns>The named container</returns>
		public Container GetContainer(string containerName)
		{
			foreach (Container container in containers)
			{
				if (container.Name == containerName)
				{
					return container;
				}
			}
			return null;
		}

		/// <summary>
		/// Returns the named control
		/// </summary>
		/// <param name="controlName">The system name of the control</param>
		/// <returns>The named control</returns>
		public Control GetControl(string controlName)
		{
			foreach (Container window in containers)
			{
				Control control = window.controls.GetControl(controlName);
				if (control != null)
				{
					return control;
				}
			}
			return null;
		}

		public void NukeHasFocus()
		{
			foreach (Container container in containers)
			{
				foreach (Control control in container.controls)
				{
					control.HasFocus = false;
				}
			}
		}

		/// <summary>
		/// Removes a container from the screen
		/// </summary>
		/// <param name="containerName">The system name of the container</param>
		/// <returns>Whether a container was deleted</returns>
		public bool RemoveContainer(string containerName)
		{
			foreach (Container window in containers)
			{
				if (window.Name == containerName)
				{
					containers.Remove(window);
					return true;
				}
			}
			return false;
		}

		/// <summary>
		/// Removes a control from the screen
		/// </summary>
		/// <param name="controlName">The system name of the control</param>
		/// <returns>Whether a control was deleted</returns>
		public bool RemoveControl(string controlName)
		{
			foreach (Container window in containers)
			{
				bool isControlRemoved = window.controls.RemoveControl(controlName);
				if (isControlRemoved)
				{
					return true;
				}
			}
			return false;
		}


		#endregion

		#region Services
		internal bool ControlNameIsFree(string controlName)
		{
			foreach (Container window in containers)
			{
				foreach (Control control in window.controls)
				{
					if (control.Name == controlName && controlName != "")
					{
						return false;
					}
				}
				foreach (Container subwindow in window.Subwindows)
				{
					ControlNameIsFreeInWindow(subwindow, controlName);
				}
			}
			return true;
		}

		private bool ControlNameIsFreeInWindow(Container window, string controlName)
		{
			foreach (Control control in window.controls)
			{
				if (control.Name == controlName && controlName != "")
				{
					return false;
				}
			}
			foreach (Container subwindow in window.Subwindows)
			{
				bool isFree = ControlNameIsFreeInWindow(subwindow, controlName);
				if (isFree == false)
				{
					return false;
				}
			}
			return true;
		}
		#endregion

		#region Inherited Method
		public void Draw()
		{
			GraphicsDevice graphicsDevice = gameRef.GraphicsDevice;
			spriteBatch.Begin();
			graphicsDevice.Clear(backgroundColor);
			spriteBatch.End();
			foreach (Container window in containers)
			{
				window.Draw(graphicsDevice, spriteBatch);
			}
			foreach (RightClickMenu menu in rightClickMenus)
			{
				menu.Draw(graphicsDevice, spriteBatch);
			}
		}
		
		public void Update(GameTime gameTime)
		{
			if (enabled)
			{
				this.gameTime = gameTime;
				inputHandler.Update();
				ScreenUpdate();
			}
		}

		public virtual void ScreenUpdate()
		{
		}

        private void OnLeftClick(object sender, MouseEventArgs e)
        {
			for (int i = rightClickMenus.Count - 1; i > -1; i--)
			{
				Control foremostControl = rightClickMenus[i];
				bool actionPerformed = foremostControl.OnLeftClick(e);
				if (actionPerformed)
				{
					return;
				}
			}
			for (int i = containers.Count - 1; i > -1; i--)
			{
				Container foremostWindow = containers[i];
				bool windowClicked = foremostWindow.OnLeftClick(e);
				if (windowClicked)
				{
					return;
				}
			}
        }

		private void OnRightClick(object sender, MouseEventArgs e)
		{
			for (int i = rightClickMenus.Count - 1; i > -1; i--)
			{
				Control foremostControl = rightClickMenus[i];
				bool actionPerformed = foremostControl.OnRightClick(e);
				if (actionPerformed)
				{
					return;
				}
			}
			for (int i = containers.Count - 1; i > -1; i--)
			{
				Container foremostWindow = containers[i];
				bool windowClicked = foremostWindow.OnRightClick(e);
				if (windowClicked)
				{
					return;
				}
			}
		}

		private void OnMouseMoved(object sender, MouseEventArgs e)
        {
			for (int i = rightClickMenus.Count - 1; i > -1; i--)
			{
				Control foremostControl = rightClickMenus[i];
				bool actionPerformed = foremostControl.OnMouseMoved(e);
				if (actionPerformed)
				{
					return;
				}
			}
			for (int i = containers.Count - 1; i > -1; i--)
			{
				Container foremostWindow = containers[i];
				bool windowMovedOver = foremostWindow.OnMouseMoved(e);
				if (windowMovedOver)
				{
					return;
				}
			}
        }

		private void OnMouseLeftDragged(object sender, MouseEventArgs e)
		{
			for (int i = rightClickMenus.Count - 1; i > -1; i--)
			{
				Control foremostControl = rightClickMenus[i];
				bool actionPerformed = foremostControl.OnMouseLeftDragged(e);
				if (actionPerformed)
				{
					return;
				}
			}
			for (int i = containers.Count - 1; i > -1; i--)
			{
				Container foremostWindow = containers[i];
				bool windowDraggedOver = foremostWindow.OnMouseLeftDragged(e);
				if (windowDraggedOver)
				{
					return;
				}
			}
		}
		#endregion
	}
}
